/*
 * @author Chris
 * @since 2023/1/27 20:15
 *
 * @brief    The project will ...
 *
 * @note     ...
 *
 * @warning  Do not to ...
 *
 * Change Logs:
 *   Date           Author       Notes
 *   2023/1/27     Chris        the first version
 */
#include "reg51.h"

#define BUZZ P1_6

__bit flagBuzzOn = 0;
unsigned char T0RH = 0;
unsigned char T0RL = 0;

extern void ConfigUART(unsigned long baud);

extern void InitLcd1602();

extern void Lcd1602ClearArea(unsigned char x, unsigned char y, unsigned char len);

extern void Lcd1602ShowStr(unsigned char x, unsigned char y, unsigned char *buf, unsigned char len);

extern void UartWrite(unsigned char *buf, unsigned char len);

extern void UartDriver();

extern void UartRxMonitor(unsigned char ms);

void ConfigTimer0(unsigned char ms);

int main() {
    EA = 1;
    ConfigTimer0(1);
    ConfigUART(9600);
    InitLcd1602();

    while (1) {
        UartDriver();
    }
}

__bit CmpMemory(unsigned char *ptr1, unsigned char *ptr2, unsigned char len) {
    while (len--) {
        if (*ptr1++ != *ptr2++) //遇到不相等数据时即刻返回 0
        {
            return 0;
        }
    }
    return 1; //比较完全部长度数据都相等则返回 1
}

void UartAction(unsigned char *buf, unsigned char len) {
    unsigned char i;
    unsigned char __code cmd0[] = "buzz on"; //开蜂鸣器命令
    unsigned char __code cmd1[] = "buzz off"; //关蜂鸣器命令
    unsigned char __code cmd2[] = "showstr "; //字符串显示命令
    unsigned char __code cmdLen[] = { //命令长度汇总表
            sizeof(cmd0) - 1, sizeof(cmd1) - 1, sizeof(cmd2) - 1,
    };
    unsigned char __code *cmdPtr[] = { //命令指针汇总表
            &cmd0[0], &cmd1[0], &cmd2[0],
    };
    for (i = 0; i < sizeof(cmdLen); i++) //遍历命令列表，查找相同命令
    {
        if (len >= cmdLen[i]) //首先接收到的数据长度要不小于命令长度
        {
            if (CmpMemory(buf, cmdPtr[i], cmdLen[i])) //比较相同时退出循环
            {
                break;
            }
        }
    }
    switch (i) //循环退出时 i 的值即是当前命令的索引值
    {
        case 0:
            flagBuzzOn = 1; //开启蜂鸣器
            break;
        case 1:
            flagBuzzOn = 0; //关闭蜂鸣器
            break;
        case 2:
            buf[len] = '\0'; //为接收到的字符串添加结束符
            Lcd1602ShowStr(0, 0, buf + cmdLen[2], len); //显示命令后的字符串
            i = len - cmdLen[2]; //计算有效字符个数
            if (i < 16) //有效字符少于 16 时，清除液晶上的后续字符位
            {
                Lcd1602ClearArea(i, 0, 16 - i);
            }
            break;
        default: //未找到相符命令时，给上机发送“错误命令”的提示
            UartWrite("bad command.\r\n", sizeof("bad command.\r\n") - 1);
            return;
    }
    buf[len++] = '\r'; //有效命令被执行后，在原命令帧之后添加
    buf[len++] = '\n'; //回车换行符后返回给上位机，表示已执行
    UartWrite(buf, len);
}

void ConfigTimer0(unsigned char ms) {
    unsigned long tmp = 65536 - ms / 1000 / (12 / 11059200) + 33;

    T0RH = (unsigned char) tmp >> 8;
    T0RL = tmp & 0xFF;
    TMOD = TMOD & 0xF0 | 0x01;
    TH0 = T0RH;
    TL0 = T0RL;

    ET0 = 1;
    TR0 = 1;
}

/**
 * 定时器 T0 中断服务函数
 */
void InterruptTimer0() __interrupt(1) {
    TH0 = T0RH;
    TL0 = T0RL;

    if (flagBuzzOn) {
        BUZZ = ~BUZZ;
    } else {
        BUZZ = 1;
    }

    UartRxMonitor(1);
}